#ifndef NYU_OBJECT_H_
#define NYU_OBJECT_H_

#include "nyu_refcnt.h"

typedef enum {
    NYU_OBJECT_TYPE_OBJECT,
    NYU_OBJECT_TYPE_STRING,
    NYU_OBJECT_TYPE_ARRAY,
    NYU_OBJECT_TYPE_NATIVE,
    NYU_OBJECT_TYPE_RANGE,
} NyuObjectType;

typedef struct NyuObject NyuObject;
typedef struct NyuString NyuString;
typedef struct NyuArray NyuArray;
typedef struct NyuNative NyuNative;

extern NyuObject *root_object;
extern long root_object_val;
extern NyuString *strval_proto;

#define NYU_OBJECT(X) ((NyuObject *)(X))

#define NYU_OBJECT_INIT(X,T,D) {                                        \
        NYU_REFCNT_INIT((X),(D));                                       \
        (X)->type = (T);                                                \
        (X)->slot_array = g_ptr_array_sized_new(5);                     \
        (X)->slots = g_hash_table_new((GHashFunc)nyu_object_get_hash_code, \
                                      (GEqualFunc)nyu_object_equals);   \
    }

struct NyuObject {
    NyuRefcnt base;
    NyuObjectType type;
    GPtrArray *slot_array;
    GHashTable *slots;

    /* virtual function */
    NyuString *(*to_string)(NyuObject *self);
    guint (*get_hash_code)(NyuObject *self);
    gboolean (*equals)(NyuObject *self, NyuObject *other);
};

NyuObject *nyu_object_new();
void nyu_object_destroy(NyuObject *self);

void nyu_object_put_slot(NyuObject *self, NyuObject *key, long val);
void nyu_object_set_slot(NyuObject *self, NyuObject *key, long val);
long nyu_object_get_slot(NyuObject *self, NyuObject *key);
long *nyu_object_get_slot_lvalue(NyuObject *self,
                                 NyuObject *key,
                                 gboolean update,
                                 gboolean create);

NyuString *nyu_object_to_string(NyuObject *self);
guint nyu_object_get_hash_code(NyuObject *self);
gboolean nyu_object_equals(NyuObject *self, NyuObject *other);

#endif /* NYU_OBJECT_H_ */
